package org.ww.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.ww.common.constant.RabbitMqConstant;
import org.ww.common.exception.StockLockException;
import org.ww.common.to.SkuWareStockTo;
import org.ww.common.to.mq.OrderTo;
import org.ww.common.to.mq.StockLockedTo;
import org.ww.common.to.mq.StockOrderTaskDetailTo;
import org.ww.common.utils.PageUtils;
import org.ww.common.utils.Query;
import org.ww.common.utils.R;
import org.ww.ware.dao.WareSkuDao;
import org.ww.ware.entity.WareOrderTaskDetailEntity;
import org.ww.ware.entity.WareOrderTaskEntity;
import org.ww.ware.entity.WareSkuEntity;
import org.ww.ware.feign.OrderFeignService;
import org.ww.ware.service.WareOrderTaskDetailService;
import org.ww.ware.service.WareOrderTaskService;
import org.ww.ware.service.WareSkuService;
import org.ww.ware.vo.OrderItemVo;
import org.ww.ware.vo.OrderVo;
import org.ww.ware.vo.WareSkuLockVo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Autowired
    private WareOrderTaskService wareOrderTaskService;
    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private OrderFeignService orderFeignService;



    private void unLockStock(Long wareId, Long skuId, Integer skuNum, Long detailId) {
        //解锁库存
        baseMapper.unLockStock(wareId,skuId,skuNum);
        //更改库存工作单状态
        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
        detailEntity.setId(detailId);
        detailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(detailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryPageByParams(Map<String, Object> params) {
        LambdaQueryWrapper<WareSkuEntity> wrapper = Wrappers.lambdaQuery();
        String skuId = (String) params.get("skuId");
        if(StringUtils.isNotBlank(skuId)){
            wrapper.eq(WareSkuEntity::getSkuId,skuId);
        }

        String wareId = (String) params.get("wareId");
        if(StringUtils.isNotBlank(wareId)){
            wrapper.eq(WareSkuEntity::getWareId,wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);

    }

    @Override
    public void purchaseUpdate(List<WareSkuEntity> wareSkuEntities) {
        if(CollectionUtils.isNotEmpty(wareSkuEntities)){
            for (WareSkuEntity wareSkuEntity : wareSkuEntities) {
                LambdaQueryWrapper<WareSkuEntity> wrapper = Wrappers.lambdaQuery();
                wrapper.eq(WareSkuEntity::getSkuId,wareSkuEntity.getSkuId());
                wrapper.eq(WareSkuEntity::getWareId,wareSkuEntity.getWareId());
                List<WareSkuEntity> wareSkuList = baseMapper.selectList(wrapper);
                if(CollectionUtils.isNotEmpty(wareSkuList)){
                    //修改
                    baseMapper.updatePruchaseUpdate(wareSkuEntity.getWareId(),wareSkuEntity.getSkuId(),wareSkuEntity.getStock());
                }else{
                    //新增
                    baseMapper.insert(wareSkuEntity);
                }

            }
        }

    }

    @Override
    public List<SkuWareStockTo> skuHasStock(List<Long> skuIds) {
        List<SkuWareStockTo> skuWareStockTos =  baseMapper.selectSkuHasStock(skuIds);
        Optional.ofNullable(skuWareStockTos)
                .orElseGet(ArrayList::new)
                .forEach(item -> {
                    item.setIsStock(item.getStock() == null ? false : item.getStock() > 0);
                });
        return skuWareStockTos;
    }

    /**
     * 为某个订单锁库存
     * @Transactional 默认运行时异常都会回滚
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = StockLockException.class)
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {

        //锁成功之后保存工作单详情，进行回溯
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        //1、找到这个商品在哪些库存中存在
        List<OrderItemVo> orderItemVos = vo.getLocks();
        List<SkuWareHasStock> skuWareHasStocks = Optional.ofNullable(orderItemVos)
                .orElseGet(ArrayList::new)
                .stream()
                .map(item -> {
                    Long skuId = item.getSkuId();
                    SkuWareHasStock skuWareHasStock = new SkuWareHasStock();
                    skuWareHasStock.setSkuId(skuId);
                    List<Long> wareIds = baseMapper.listWareIdHasSkuStock(skuId);
                    skuWareHasStock.setWareIds(wareIds);
                    skuWareHasStock.setNum(item.getCount());
                    return skuWareHasStock;
                }).collect(Collectors.toList());

        //2、锁定库存
        for (SkuWareHasStock skuWareHasStock : skuWareHasStocks) {
            Boolean skuStocked = false;
            Long skuId = skuWareHasStock.getSkuId();
            List<Long> wareIds = skuWareHasStock.getWareIds();
            if(CollectionUtils.isEmpty(wareIds)){
                //没有任何仓库有这个库存的商品
                throw new StockLockException(skuId);
            }
            for (Long wareId : wareIds) {
                //成功返回1 失败返回0
                int count = baseMapper.lockSkuStock(skuId,wareId,skuWareHasStock.getNum());
                if(count == 1){
                    /**
                     * 锁定成功我们发送mq消息：
                     * 如果在锁其他库存的时候出错，业务库存就会回滚，之前的mq消息也发送出去了，消息过期后会通过id进行查询，查询不到也就不用处理
                     */
                    skuStocked = true;
                    //保存锁单成功详情
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null, skuId, null, skuWareHasStock.getNum(), wareOrderTaskEntity.getId(), wareId, 1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    //锁单成功之后向mq发送消息
                    StockLockedTo lockedTo = new StockLockedTo();

                    StockOrderTaskDetailTo stockOrderTaskDetailTo = new StockOrderTaskDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockOrderTaskDetailTo);
                    lockedTo.setId(wareOrderTaskEntity.getId());
                    lockedTo.setDetailTo(stockOrderTaskDetailTo);
                    rabbitTemplate.convertAndSend(RabbitMqConstant.STOCK_EVENT_EXCHANGE,RabbitMqConstant.STOCK_LOCKED_BINDING,lockedTo);
                    break;
                }else{
                    //当前仓库锁定失败，重试下一个仓库

                }
            }
            if(!skuStocked){
                //当前商品所有仓库都没有锁住
                throw new StockLockException(skuId);
            }
        }

        return true;
    }

    @Override
    public void stockUnLock(StockLockedTo to) {
        Long id = to.getId();
        StockOrderTaskDetailTo detailTo = to.getDetailTo();
        Long detailId = detailTo.getId();
        /**
         * 拿到detailId一定要去库存工作单中进行查询，看数据是否存在
         * 数据存在：查询订单是否存在：
         *      1、订单不存在：需要进行解锁
         *      2、订单存在：需要判断订单状态
         *          1、取消状态：需要解锁
         *          2、已支付：无需解锁
         * 数据不存在：锁库存本身业务有问题，无需处理
         */
        WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailId);
        if(ObjectUtils.isNotEmpty(detailEntity)){
            WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getById(id);
            R r = orderFeignService.getByOrderSn(orderTaskEntity.getOrderSn());
            if(r.getCode() == 0){
                OrderVo data = r.getData(new TypeReference<OrderVo>(){});
                //如果订单的状态取消或者数据为空让消息重回队列
                if(data == null || data.getStatus() == 4){
                    //解锁
                    //当前状态只有是已锁定的情况下才能解锁
                    if(detailEntity.getLockStatus() == 1){
                        unLockStock(detailEntity.getWareId(),detailEntity.getSkuId(),detailEntity.getSkuNum(),detailId);
                    }
                    //第二个参数是否确认本批次信息
                }
            }else{
                //远程调用失败
                throw new RuntimeException("远程调用失败");
            }
        }else {
            //无需解锁
        }
    }

    /**
     * 防止用户下单之后订单超时，更改订单状态为取消的时候，订单服务崩溃，然后解锁库存的时候判断订单状态不符合规则就不会去解锁库存
     * 这里在取消订单的时候主动触发解锁库存服务
     * @param to
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unLockStock(OrderTo to) {
        String orderSn = to.getOrderSn();
        WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getByOrderSn(orderSn);
        Long taskId = orderTaskEntity.getId();
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(
                new LambdaQueryWrapper<WareOrderTaskDetailEntity>()
                        .eq(WareOrderTaskDetailEntity::getTaskId, taskId)
                        .eq(WareOrderTaskDetailEntity::getLockStatus, 1));
        for (WareOrderTaskDetailEntity detailEntity : list) {
            unLockStock(detailEntity.getWareId(),detailEntity.getSkuId(),detailEntity.getSkuNum(),detailEntity.getId());
        }

    }

    @Data
    class SkuWareHasStock{
        private Long skuId;
        private Integer num;
        private List<Long> wareIds;
    }
}
